package com.demo.webflux.spring5core.framework.webmvc;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

import com.demo.webflux.spring5core.framework.annotation.RequestParam;
import com.demo.webflux.spring5core.mock.HttpServletRequest;
import com.demo.webflux.spring5core.mock.HttpServletResponse;

/**用来动态匹配method参数，包括类转换、动态赋值
 * 原生spring的HandlerAdapter主要完成请求传递到服务端的参数列表与method实参列表的对应关系，完成参数值的类型转换工作。
 * 核心方法是handler()，在handler()方法中用反射来调用被适配的目标方法，并将转换包装好的参数列表传递过去
 * @author zhangsulei
 *
 */
public class HandlerAdapter {

	public boolean supports(Object handler) {
		return handler instanceof HandlerMapping;
	}

	public ModelAndView handle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
		HandlerMapping handlerMapping = (HandlerMapping)handler;

		// 每个方法有一个参数列表，这里保存的是形参列表
		Map<String, Integer> paramMapping = new HashMap<>();

		// 这里只是给出命名参数
		Annotation[][] pa = handlerMapping.getMethod().getParameterAnnotations();
		for (int i=0; i<pa.length; i++) {
			for (Annotation a : pa[i]) {
				if(a instanceof RequestParam) {
					String paramName = ((RequestParam)a).value();
					if(!"".equals(paramName.trim())) {
						paramMapping.put(paramName, i);
					}
				}
			}
		}
		// 提取方法中request和response参数
		Class<?>[] paramTypes = handlerMapping.getMethod().getParameterTypes();
		for (int i = 0; i < paramTypes.length; i++) {
			Class<?> type = paramTypes[i];
			if(type == HttpServletRequest.class || type == HttpServletResponse.class) {
				paramMapping.put(type.getName(), i);
			}
		}

		// 得到自定义命名参数所在的位置，用户通过url传过来的参数列表
		Map<String, String[]> reqParameterMap = req.getParameterMap();
		// 实参列表
		Object[] paramValues = new Object[paramTypes.length];

		for (Map.Entry<String, String[]> param : reqParameterMap.entrySet()) {
			String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll("\\s", ",");
			if(!paramMapping.containsKey(param.getKey())) {
				continue;
			}
			int index = paramMapping.get(param.getKey());
			// 因为页面传过来的值都是string类型的，而在方法中定义的类型是千变万化的，所以要针对我们传过来的参数进行类型转换
			paramValues[index] = caseStringValue(value, paramTypes[index]);
		}

		if(paramMapping.containsKey(HttpServletRequest.class.getName())) {
			int reqIndex = paramMapping.get(HttpServletRequest.class.getName());
			paramValues[reqIndex] = req;
		}
		if(paramMapping.containsKey(HttpServletResponse.class.getName())) {
			int respIndex = paramMapping.get(HttpServletResponse.class.getName());
			paramValues[respIndex] = resp;
		}

		// 从handler中取出controller和method，然后利用反射机制进行调用
		Object result = handlerMapping.getMethod().invoke(handlerMapping.getController(), paramValues);
		if(result == null) {
			return null;
		}

		boolean isModelAndView = handlerMapping.getMethod().getReturnType() == ModelAndView.class;
		if(isModelAndView) {
			return (ModelAndView)result;
		}else {
			return null;
		}
	}

	private Object caseStringValue(String value, Class<?> clazz) {
		if(String.class == clazz) {
			return value;
		}else if(Integer.class == clazz) {
			return Integer.valueOf(value);
		}else if(int.class == clazz) {
			return Integer.valueOf(value).intValue();
		}else {
			// 如果还有其他类型，继续处理
			return null;
		}
	}

}
